Popular Searches
Popular Course Categories
Popular Courses

Managing Environment Variables, API Configuration, and Application Security

Managing Environment Variables, API Configuration, and Application Security

Third-Party Integrations and Application Architecture

Managing Environment Variables, API Configuration, and Application Security in React Native

Course: React Native Training

Topic: Managing Environment Variables, API Configuration, and Application Security

Course Link: React Native Training at JustAcademy

Demo Link: Register for a Course Demo

JustAcademy Website: Visit JustAcademy


1. Introduction

React Native applications commonly communicate with remote servers to retrieve data, authenticate users, upload files, process payments, and perform other business operations. To support these features safely and reliably, developers must manage environment variables, API configuration, and application security.

Environment variables help configure applications for different environments, such as development, testing, staging, and production. API configuration centralizes details such as base URLs, request headers, timeouts, and error handling. Application security protects sensitive information, user data, API communications, and access to backend services.

These practices are closely related. Incorrect configuration can send production users to a testing server, while insecure storage or exposed credentials can allow unauthorized access to application resources.

Learning Objectives

  • Understand environment variables and their role in React Native applications.
  • Configure separate development, staging, and production environments.
  • Manage API base URLs and request configuration centrally.
  • Use environment-specific settings without hardcoding values throughout the application.
  • Understand the limitations of secrets stored inside mobile applications.
  • Secure authentication tokens and sensitive local data.
  • Implement secure API communication, validation, and error handling.
  • Apply practical security checks before releasing an application.

2. Understanding Environment Variables

2.1 What Are Environment Variables?

Environment variables are configuration values supplied to an application or build process. They allow developers to change application behavior without manually editing many source files.

For example, a React Native application may need different API endpoints for development and production. Instead of changing the endpoint in multiple components, the application can read a centralized configuration value.

Development API:
https://dev-api.example.com

Staging API:
https://staging-api.example.com

Production API:
https://api.example.com

2.2 Why Are Environment Variables Important?

  • Environment separation: Each deployment can use its own backend services.
  • Maintainability: Configuration is managed in a consistent location.
  • Reduced hardcoding: Developers avoid repeating environment-specific values across files.
  • Deployment flexibility: Build pipelines can supply appropriate configuration values.
  • Fewer configuration mistakes: Centralized settings make it easier to review endpoints and feature flags.

2.3 Common Configuration Values

Configuration Example Purpose
API base URL https://api.example.com Identifies the backend API.
Environment name production Identifies the current build environment.
Request timeout 15000 milliseconds Defines how long a request may wait.
Feature flag true or false Controls whether a feature is enabled.
Public service identifier Public project ID Identifies a service when its design permits public client-side use.
Private API secret Confidential credential Must remain on a trusted server, not in a mobile app.

3. Environment Variables and Mobile Application Security

3.1 An Important Security Principle

React Native applications run on devices controlled by their users. Values bundled into a mobile application may be extracted through application-package inspection, reverse engineering, debugging, or runtime analysis.

Important: An environment variable is a configuration mechanism, not a security vault. Putting a password, private API key, database credential, or signing secret in an environment file does not make it safe once that value is included in a distributed application.

3.2 Public Configuration vs. Private Secrets

Value Can it be bundled in the app? Recommended handling
Public API base URL Generally yes Use environment-specific configuration.
Public analytics identifier Often yes, depending on the provider Restrict usage through provider controls where available.
Feature flag Usually yes Do not use it as the sole authorization control.
User access token May exist at runtime Store and transmit securely; use short lifetimes and revocation where supported.
Private API key No Keep it on a trusted backend.
Database administrator password No Keep it in backend secret management.
Payment provider secret key No Perform privileged payment operations on the server.
Private signing key No Protect it in an appropriate secure signing environment.

3.3 Example of an Insecure Approach

// Insecure: do not put private credentials in mobile source code.
const API_SECRET = "my-private-server-secret";
const DATABASE_PASSWORD = "my-database-password";

Even if these values are moved into a .env file, they remain exposed if the build embeds them in the application.

3.4 Recommended Architecture

React Native Mobile App
        |
        | HTTPS request with user authentication
        v
Trusted Backend API
        |
        | Validates user permissions
        | Uses server-side credentials
        v
Database / Payment Provider / Private Services

The mobile app should use only the client-side configuration it needs. The backend should hold privileged credentials, validate authorization, enforce business rules, and interact with protected services.

4. Setting Up Environment Configuration

4.1 Choose a Configuration Strategy

The correct setup depends on whether the project uses React Native Community CLI, Expo, or a custom build pipeline. Before adding a package, confirm the project type and its current configuration system.

  • Expo: Use Expo app configuration and environment variables supported by the project's Expo tooling.
  • React Native Community CLI: Use an appropriate environment-configuration library or build-time configuration mechanism.
  • CI/CD pipelines: Supply environment-specific values through controlled build settings.

Do not mix configuration instructions from unrelated tooling without checking compatibility with the installed React Native version.

4.2 Example Project Structure

MyReactNativeApp/
├── src/
│   ├── config/
│   │   └── env.ts
│   ├── services/
│   │   └── api.ts
│   ├── screens/
│   ├── components/
│   └── utils/
├── .env.development
├── .env.staging
├── .env.production
├── .gitignore
└── package.json

This structure is an example rather than a requirement. Some frameworks load environment values through their own configuration files, and some build systems use separate configuration profiles instead of multiple .env files.

4.3 Example Environment Files

A project using a compatible environment-variable loader might define public configuration values like the following.

.env.development

API_BASE_URL=https://dev-api.example.com
APP_ENV=development
API_TIMEOUT_MS=15000

.env.staging

API_BASE_URL=https://staging-api.example.com
APP_ENV=staging
API_TIMEOUT_MS=15000

.env.production

API_BASE_URL=https://api.example.com
APP_ENV=production
API_TIMEOUT_MS=15000

These files contain only non-secret configuration. The exact loading syntax and variable exposure rules depend on the framework or library in use. For example, Expo projects commonly expose intended client-side variables using the EXPO_PUBLIC_ prefix. Such variables are public and must never contain secrets.

4.4 Protect Local Environment Files

If your chosen configuration system uses local .env files, review the project's .gitignore file.

# Local environment files
.env
.env.*
!.env.example

This example ignores local environment files while allowing a template file to be tracked. Adjust the pattern if your project intentionally tracks a particular non-secret environment file. Git ignore rules do not remove files already tracked by Git and do not protect values embedded in a compiled app.

4.5 Create a Safe Example Configuration

A template helps other developers understand which values are required without exposing sensitive information.

# .env.example
API_BASE_URL=https://api.example.com
APP_ENV=development
API_TIMEOUT_MS=15000

Document required variables in the project's setup instructions. Avoid including actual passwords, production credentials, or private keys in the template.

5. Centralizing Environment Configuration

5.1 Why Centralize Configuration?

Reading environment values throughout screens and components can make an application difficult to maintain. A configuration module provides one place to validate required values, define defaults, and expose consistent settings.

5.2 Example Configuration Module

The following example assumes your selected environment system makes these variables available through process.env. The mechanism that supplies those values must be configured separately for your project.

// src/config/env.ts
const apiBaseUrl = process.env.API_BASE_URL;
const appEnvironment = process.env.APP_ENV ?? "development";
const timeoutValue = process.env.API_TIMEOUT_MS ?? "15000";

if (!apiBaseUrl) {
  throw new Error("API_BASE_URL is not configured");
}

const parsedTimeout = Number(timeoutValue);

if (!Number.isFinite(parsedTimeout) || parsedTimeout <= 0) {
  throw new Error("API_TIMEOUT_MS must be a positive number");
}

export const env = {
  apiBaseUrl: apiBaseUrl.replace(/\/+$/, ""),
  appEnvironment,
  apiTimeoutMs: parsedTimeout,
};

Depending on the bundler and configuration library, direct access to process.env may not work automatically in a React Native app. Adapt the value-access syntax to the selected environment tool.

5.3 Benefits of Validation

  • Detects missing configuration early.
  • Prevents requests from using an undefined or malformed endpoint.
  • Ensures numeric settings are parsed correctly.
  • Makes configuration errors easier to diagnose during development and testing.
  • Reduces accidental reliance on inappropriate fallback values.

5.4 Avoid Unsafe Production Defaults

// Risky pattern:
const API_URL =
  process.env.API_BASE_URL || "https://production.example.com";

If the intended configuration is missing, this fallback could silently send a development build to production. Prefer explicit validation and fail early when required settings are absent.

6. Managing Multiple Application Environments

6.1 Development Environment

The development environment is used while building features and debugging application behavior.

  • Connect to a development backend or local test server.
  • Use test accounts and synthetic data wherever possible.
  • Enable development diagnostics only when appropriate.
  • Keep private service credentials on the backend even during development.

6.2 Staging Environment

Staging should resemble production closely enough to test the release process before deployment.

  • Use staging API endpoints and staging service accounts.
  • Test authentication, authorization, error handling, and network failures.
  • Confirm that the build does not use production credentials accidentally.
  • Validate app links, push notifications, analytics, and third-party integrations.

6.3 Production Environment

Production is the environment used by actual customers.

  • Use the approved production API endpoint.
  • Disable development-only menus and verbose diagnostics.
  • Never expose private server credentials in the app bundle.
  • Verify TLS certificate handling and backend authorization.
  • Use controlled release processes and monitor errors without logging sensitive data.

6.4 Environment Comparison

Feature Development Staging Production
Backend Development server Staging server Production server
Data Test data Representative test data Real user data
Debugging Detailed diagnostics Controlled diagnostics Restricted diagnostics
Credentials Development-only credentials on trusted services Staging-only credentials on trusted services Production credentials in approved secret storage
Release validation Feature development Pre-release testing Live monitoring

7. API Configuration in React Native

7.1 What Is API Configuration?

API configuration defines how the application communicates with backend services. It can include:

  • Base API URL.
  • Default request headers.
  • Request timeout settings.
  • Authentication handling.
  • Serialization and response parsing.
  • Network error handling.
  • Request identifiers and safe diagnostic logging.

7.2 Centralize API Endpoints

Instead of hardcoding a complete URL in every screen, create a shared API client.

// src/services/api.ts
import { env } from "../config/env";

type RequestOptions = {
  method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
  headers?: Record<string, string>;
  body?: string;
  accessToken?: string;
};

export async function apiRequest<T>(
  path: string,
  options: RequestOptions = {}
): Promise<T> {
  const normalizedPath = path.replace(/^\/+/, "");
  const url = `${env.apiBaseUrl}/${normalizedPath}`;

  const controller = new AbortController();
  const timeoutId = setTimeout(
    () => controller.abort(),
    env.apiTimeoutMs
  );

  try {
    const response = await fetch(url, {
      method: options.method ?? "GET",
      headers: {
        Accept: "application/json",
        ...(options.body ? { "Content-Type": "application/json" } : {}),
        ...(options.accessToken
          ? { Authorization: `Bearer ${options.accessToken}` }
          : {}),
        ...options.headers,
      },
      body: options.body,
      signal: controller.signal,
    });

    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}`);
    }

    return (await response.json()) as T;
  } finally {
    clearTimeout(timeoutId);
  }
}

This example demonstrates centralized request configuration. A production implementation should also distinguish network failures, timeouts, non-JSON responses, and HTTP errors. It should avoid returning raw server diagnostics to users and should use a well-defined API response schema.

Important: This basic helper is not a complete production networking layer. If it is used for user-specific requests, the calling code must obtain and supply a valid token securely, and the server must independently verify the token and permissions.

7.3 Example API Usage

// src/services/userService.ts
import { apiRequest } from "./api";

export type UserProfile = {
  id: string;
  name: string;
  email: string;
};

export async function getUserProfile(
  accessToken: string
): Promise<UserProfile> {
  return apiRequest<UserProfile>("users/me", {
    method: "GET",
    accessToken,
  });
}

Keeping endpoint calls in service modules makes the application easier to test and maintain. It also reduces duplicated URL construction across screens.

7.4 URL Construction Best Practices

  • Normalize trailing slashes in the base URL.
  • Use consistent endpoint naming.
  • Encode query parameters using URLSearchParams where supported.
  • Avoid concatenating untrusted user input directly into URL paths.
  • Use HTTPS for production API communication.
  • Validate configured URLs as part of the build and release process.

8. HTTP Methods, Headers, and API Requests

8.1 Common HTTP Methods

Method Typical purpose Example
GET Retrieve information Fetch a user profile.
POST Create a resource or initiate an operation Create an order.
PUT Replace a resource representation Replace profile details.
PATCH Update selected fields Update a display name.
DELETE Delete a resource Remove a saved address.

8.2 Common Headers

Accept: application/json
Content-Type: application/json
Authorization: Bearer <access-token>

Only send headers required by the endpoint. Never include passwords, access tokens, or private credentials in URL query parameters because URLs can be recorded in logs, analytics, proxies, or browser-like histories.

8.3 Sending JSON Data

const result = await apiRequest<{ id: string }>("tasks", {
  method: "POST",
  accessToken,
  body: JSON.stringify({
    title: "Prepare project report",
    completed: false,
  }),
});

Client-side validation improves usability, but the backend must validate the request independently. A modified client can send data that does not match the app's normal form rules.

9. Managing API Timeouts and Network Failures

9.1 Why Timeouts Matter

Mobile devices frequently switch between Wi-Fi and cellular networks. A request can stall because of weak connectivity, a server outage, a DNS problem, or a slow operation. Without a timeout policy, the user may wait longer than expected.

9.2 Handling Timeout and Network Errors

try {
  const profile = await getUserProfile(accessToken);
  // Update application state with the profile.
} catch (error) {
  if (error instanceof Error && error.name === "AbortError") {
    // Show a timeout message.
  } else {
    // Show a safe network or request error.
  }
}

Depending on the networking implementation, a timeout may be reported through a platform-specific error shape rather than a consistent error name. Verify the behavior on the target iOS and Android versions.

9.3 Recommended Error Categories

  • Connectivity error: The device cannot reach the server.
  • Timeout: The request exceeded the configured duration.
  • Authentication error: The credentials are missing, invalid, or expired.
  • Authorization error: The user lacks permission to perform an operation.
  • Validation error: The submitted data is invalid.
  • Server error: The backend failed to complete the request.

9.4 Retry Carefully

Retries can help with temporary network failures, but they should be designed deliberately.

  • Use bounded retries with a delay for eligible transient failures.
  • Avoid retrying indefinitely.
  • Use exponential backoff and jitter for suitable retry policies.
  • Do not blindly retry payment creation or other non-idempotent operations.
  • Use server-supported idempotency keys where appropriate.
  • Do not retry authentication or validation errors without addressing the underlying problem.

10. Authentication and Authorization

10.1 Authentication vs. Authorization

Authentication establishes who the user is. Authorization determines what that authenticated user is allowed to do.

For example, a user may successfully sign in but still be prohibited from accessing an administrator endpoint. The backend must enforce that restriction on every relevant request.

10.2 Token-Based Authentication

A common pattern is for the backend to issue a short-lived access token after successful authentication. The app attaches the token to requests that require the user's identity.

Authorization: Bearer <access-token>

The server validates the token and checks the user's permissions. Merely hiding an application screen or button is not a security boundary.

10.3 Token Management Recommendations

  • Prefer short-lived access tokens where supported.
  • Use a carefully designed refresh-token flow if required.
  • Store sensitive tokens using platform-backed secure storage.
  • Remove local authentication data when the user signs out.
  • Support server-side token revocation or session invalidation where appropriate.
  • Avoid writing tokens to logs, analytics, crash reports, or navigation parameters.
  • Handle expired sessions without creating infinite request loops.

11. Secure Local Storage

11.1 Different Storage Options

Storage type Suitable uses Security considerations
AsyncStorage Non-sensitive preferences and cached application data Not designed as secure storage for secrets.
SecureStore or platform keychain/keystore integrations Appropriate tokens and small sensitive values Review platform behavior, backup rules, accessibility, and library configuration.
In-memory state Temporary data during an active session Can be lost when the process ends; does not prevent runtime inspection on a compromised device.
Backend storage Private credentials and authoritative business records Requires server-side access control, encryption, monitoring, and secret management.

11.2 Avoid Storing Tokens in AsyncStorage

// Avoid for sensitive authentication tokens:
await AsyncStorage.setItem("accessToken", accessToken);

AsyncStorage is useful for ordinary persistence, but it should not be treated as an encrypted credential vault. Choose secure storage suitable for the project's framework and threat model.

11.3 Example with Expo SecureStore

In an Expo project, the expo-secure-store package can store suitable small values using platform-backed secure storage. Install and configure the package according to the Expo SDK used by the project.

import * as SecureStore from "expo-secure-store";

export async function saveAccessToken(token: string) {
  await SecureStore.setItemAsync("accessToken", token);
}

export async function readAccessToken() {
  return SecureStore.getItemAsync("accessToken");
}

export async function removeAccessToken() {
  await SecureStore.deleteItemAsync("accessToken");
}

This example is Expo-specific. For React Native Community CLI projects, select a maintained secure-storage library compatible with the project's React Native version. Secure storage reduces exposure at rest, but it does not make a token impossible to extract from a compromised or instrumented device.

12. Secure API Communication

12.1 Use HTTPS

HTTPS protects data in transit through encryption and server authentication. Production applications should communicate with trusted HTTPS endpoints and should not disable certificate validation to work around connection problems.

12.2 Avoid Insecure HTTP

// Avoid for production APIs:
const apiUrl = "http://api.example.com";

// Prefer:
const apiUrl = "https://api.example.com";

Development servers may require special local-network configuration, but release builds should not unintentionally permit cleartext traffic to sensitive endpoints.

12.3 Certificate Validation and Pinning

Native networking stacks ordinarily validate certificates using platform trust rules. Certificate pinning can provide additional protection in specific threat models, but it adds operational complexity. Incorrect pinning can lock users out after certificate or key changes.

If pinning is required, implement it using a suitable native-compatible solution, define a backup and rotation strategy, and test certificate changes before release. Do not replace normal certificate validation with a permissive custom handler.

13. Secure Backend and API Design

13.1 Never Trust the Mobile Client

A React Native app can be modified or reverse engineered. The backend must assume that requests may come from a manipulated client and enforce security rules independently.

  • Validate every incoming field on the server.
  • Check ownership before returning or modifying a resource.
  • Enforce role-based or permission-based access control.
  • Apply rate limits to sensitive or expensive endpoints.
  • Use server-side authorization for payments and account changes.
  • Return only the data the user is authorized to access.

13.2 Example of Server-Side Authorization

// Conceptual backend pseudocode
async function getOrderForUser(request) {
  const user = await authenticateRequest(request);
  const order = await database.orders.findById(request.params.orderId);

  if (!order) {
    return { status: 404, body: { message: "Order not found" } };
  }

  if (order.userId !== user.id && !user.permissions.includes("orders:read:any")) {
    return { status: 403, body: { message: "Forbidden" } };
  }

  return { status: 200, body: order };
}

This is conceptual pseudocode, not a complete backend implementation. Production code must also handle validation, database errors, audit logging, and the chosen framework's response conventions.

13.3 Do Not Rely on CORS for Mobile API Security

Cross-Origin Resource Sharing (CORS) is primarily a browser-enforced mechanism. A native React Native client does not rely on browser CORS enforcement in the same way a web application does. CORS settings therefore cannot replace authentication, authorization, rate limiting, or server-side validation.

14. Secure Handling of Third-Party API Keys

14.1 Understand the Provider's Key Model

Some providers issue public client identifiers that are designed to be included in an app. Others issue privileged secret keys that must never be distributed to clients. Read the provider's documentation to determine which type you have.

14.2 Recommended Controls

  • Keep privileged keys on the backend.
  • Use platform or application restrictions for public client keys when supported.
  • Restrict permissions to the minimum required.
  • Set usage limits and alerts where available.
  • Rotate exposed credentials promptly.
  • Monitor unexpected usage and investigate anomalies.
  • Never assume a value is secret merely because its variable name includes KEY or SECRET.

14.3 Example Secure Service Flow

React Native App
    |
    | Sends authenticated request
    v
Backend Service
    |
    | Reads private key from secret manager
    v
Third-Party Provider

The backend should verify the user's permissions and validate the requested operation before using the private key.

15. Logging, Debugging, and Error Reporting

15.1 Avoid Sensitive Logs

Logs help developers investigate problems, but they can also expose user data and credentials if they contain request headers, tokens, passwords, or full API responses.

// Avoid:
console.log("Authorization token:", accessToken);
console.log("Login request:", { email, password });

// Prefer:
console.info("Login request started");
console.info("Login request completed", { status: "success" });

15.2 Production Logging Practices

  • Do not log passwords, authentication tokens, private keys, or full payment details.
  • Redact personal information from diagnostics where possible.
  • Use structured error codes and request identifiers.
  • Limit access to logs and define retention periods.
  • Disable development debugging tools in release builds when appropriate.
  • Review third-party analytics and crash-reporting settings.

15.3 Safe Error Messages

Display messages that help users recover without revealing internal implementation details.

Technical problem Example user-facing message
Network unavailable Check your internet connection and try again.
Session expired Your session has expired. Please sign in again.
Validation failed Please check the information you entered.
Server failure We could not complete your request. Please try again later.

16. Input Validation and Data Protection

16.1 Validate User Input

Client-side validation can prevent accidental mistakes and improve user experience. However, all security-sensitive validation must also be performed by the backend.

  • Check required fields and data types.
  • Validate identifiers, dates, numeric ranges, and expected formats.
  • Limit input lengths where appropriate.
  • Reject unexpected fields on the server when suitable.
  • Use parameterized database queries or safe ORM patterns on the backend.
  • Encode output appropriately when displaying untrusted content.

16.2 Avoid Sensitive Data in Navigation and State

Navigation parameters, global state, analytics events, and debug snapshots may be exposed to debugging tools or logging systems. Avoid passing passwords, refresh tokens, private keys, and other secrets through these mechanisms.

16.3 Minimize Data Collection

Collect and retain only the data needed for the application's purpose. Define access controls and retention policies, and avoid storing sensitive information indefinitely on the device.

17. Secure Application Configuration in Expo and React Native CLI

17.1 Expo Configuration

Expo supports environment variables and app configuration, but developers must understand which values are included in the client bundle. Variables prefixed with EXPO_PUBLIC_ are intended for client-side access and are not secret.

// Example .env
EXPO_PUBLIC_API_BASE_URL=https://api.example.com
// Example usage in an Expo app
const apiBaseUrl = process.env.EXPO_PUBLIC_API_BASE_URL;

if (!apiBaseUrl) {
  throw new Error("Missing API base URL");
}

Use the environment-loading behavior documented for your installed Expo tooling. Do not place server-only credentials in public variables or app configuration fields that are shipped to the client.

17.2 React Native Community CLI

React Native Community CLI projects do not automatically support every environment-variable workflow. Depending on the build setup, developers may use a compatible environment-variable package, native build configuration, or a controlled build-time configuration process.

Check that the chosen library supports the current React Native architecture and build tools. Configure Android and iOS builds consistently and verify the final compiled application rather than assuming that a local environment file is automatically loaded.

17.3 Keep Platform Configuration Consistent

  • Verify API endpoints in both Android and iOS release builds.
  • Review Android cleartext traffic settings.
  • Review iOS App Transport Security settings.
  • Check deep links, app identifiers, and redirect URLs for each environment.
  • Ensure release builds do not include unintended development endpoints or debug tools.

18. Managing Environment Variables in CI/CD

18.1 What Is CI/CD?

Continuous Integration and Continuous Delivery or Deployment (CI/CD) automate building, testing, signing, and distributing applications. A controlled CI/CD pipeline can supply environment-specific configuration and reduce manual release mistakes.

18.2 Recommended Pipeline

  1. Check out the approved source revision.
  2. Install dependencies using the project's lockfile.
  3. Run linting, type checks, and automated tests.
  4. Load the appropriate environment configuration.
  5. Validate required settings and reject invalid endpoints.
  6. Build the application using the selected release profile.
  7. Run release checks and security scans.
  8. Sign and distribute the approved build.
  9. Monitor release health and investigate reported issues.

18.3 Store CI/CD Secrets Properly

Use your CI/CD provider's protected secret storage for build credentials and other sensitive values. Restrict access to trusted workflows and contributors. Avoid printing secrets to logs or writing them into publicly accessible build artifacts.

Remember that secrets needed only by the backend should remain on the backend. If a build process embeds a credential into the mobile binary, the resulting application may expose it to users.

19. Feature Flags and Remote Configuration

19.1 What Is a Feature Flag?

A feature flag controls whether a feature is enabled. Flags can help teams release features gradually, test behavior, or disable a problematic feature without immediately publishing a new application version.

type AppFeatures = {
  enableNewDashboard: boolean;
};

export const appFeatures: AppFeatures = {
  enableNewDashboard: false,
};

This static example is suitable for simple build-time configuration. A remote configuration system can be used when values need to change without rebuilding the app.

19.2 Security Considerations

  • Do not use a feature flag as the only access-control mechanism.
  • Assume client-side flag values can be inspected or modified.
  • Enforce privileged operations on the backend.
  • Define safe defaults for unavailable remote configuration.
  • Validate remote values before using them.
  • Do not allow remote configuration to execute arbitrary untrusted code.

20. Common Security Mistakes and How to Fix Them

Mistake Risk Recommended fix
Hardcoding private API keys Credential theft and unauthorized service use Move privileged operations to the backend.
Assuming .env values are secret Secrets may be embedded in the app bundle Classify every value before including it in the client.
Using the production API in development Test operations may affect real data Validate environment-specific endpoints.
Storing tokens in ordinary persistent storage Increased exposure of sensitive data at rest Use suitable platform-backed secure storage.
Logging authorization headers Tokens may leak through diagnostics Redact sensitive values and review logging.
Trusting client-side role checks Unauthorized API access Enforce permissions on the backend.
Disabling TLS certificate checks Communication may be vulnerable to interception Use normal platform certificate validation.
Retrying all failed requests Duplicate operations or repeated load Use bounded, status-aware retries and idempotency where appropriate.
Using insecure fallback configuration Requests may go to an unintended server Validate required configuration and fail early.
Ignoring dependency updates Known vulnerabilities may remain unresolved Review and update dependencies through a tested process.

21. Testing Environment Configuration and Application Security

21.1 Configuration Tests

  • Confirm required environment variables are present.
  • Confirm malformed API URLs are rejected.
  • Verify that development, staging, and production builds use the expected endpoints.
  • Check that timeouts and feature flags have valid values.
  • Confirm that missing configuration does not silently point to production.

21.2 API Integration Tests

  • Test successful requests and expected response formats.
  • Test expired, missing, and invalid authentication tokens.
  • Test permission-denied responses.
  • Test malformed input and validation errors.
  • Test server errors, timeouts, and network disconnections.
  • Verify that retries do not create duplicate operations.

21.3 Security Tests

  • Inspect release artifacts for accidental secret exposure.
  • Check logs and crash reports for sensitive values.
  • Confirm that backend endpoints enforce authorization.
  • Review third-party SDK permissions and data collection.
  • Check that private credentials are absent from source control and client bundles.
  • Verify that release builds do not use insecure network settings.

22. Practical Project: Secure API Configuration Module

Project Objective

Create a React Native application that uses environment-specific API configuration, a centralized API client, secure token storage, and consistent error handling.

Project Requirements

  • Use separate development, staging, and production configurations.
  • Centralize the API base URL and request timeout.
  • Validate required configuration during startup or build setup.
  • Create an API service for fetching a user profile.
  • Store appropriate authentication tokens using secure storage.
  • Handle authentication failures, server errors, and network timeouts.
  • Ensure that private service credentials remain on the backend.

Step 1: Define Public Configuration

// Example conceptual configuration
export const appConfig = {
  apiBaseUrl: process.env.API_BASE_URL,
  environment: process.env.APP_ENV,
  timeoutMs: Number(process.env.API_TIMEOUT_MS ?? "15000"),
};

Adapt the variable-loading mechanism to the project's framework and validate the final values before use.

Step 2: Build the API Service

// src/services/profileService.ts
import { apiRequest } from "./api";

export type Profile = {
  id: string;
  name: string;
};

export function fetchProfile(token: string) {
  return apiRequest<Profile>("users/me", {
    method: "GET",
    accessToken: token,
  });
}

Step 3: Retrieve the Token Securely

import * as SecureStore from "expo-secure-store";
import { fetchProfile } from "./services/profileService";

export async function loadProfile() {
  const token = await SecureStore.getItemAsync("accessToken");

  if (!token) {
    throw new Error("Authentication required");
  }

  return fetchProfile(token);
}

This example uses Expo SecureStore. Use the appropriate secure-storage library if the project is based on React Native Community CLI.

Step 4: Handle Errors in the Screen

try {
  const profile = await loadProfile();
  // Update the UI with the returned profile.
} catch {
  // Display a safe message and offer recovery.
  // Do not log tokens or sensitive response data.
}

Step 5: Validate the Release

  1. Build the development version and verify the development API.
  2. Build the staging version and run integration tests.
  3. Inspect the release bundle for unintended private credentials.
  4. Confirm HTTPS communication and server-side authorization.
  5. Verify logout removes the stored authentication data.
  6. Build the production version and confirm its configuration.

Expected Outcome

The completed project should have a centralized and validated configuration system, reusable API services, appropriate token storage, reliable error handling, and a clear separation between public client configuration and private backend secrets.

23. Troubleshooting Guide

Problem Possible cause Solution
Environment variable is undefined Incorrect loader or variable name Verify the environment tooling and rebuild the app as required.
App calls the wrong server Incorrect build profile or cached configuration Inspect the active build configuration and rebuild with the correct profile.
Requests fail on a physical device Device cannot reach the development server Check network reachability, firewall rules, and server binding.
Requests fail because of TLS Invalid certificate, hostname mismatch, or trust issue Fix the server certificate chain and hostname; do not disable validation.
API returns 401 Missing, invalid, or expired token Check authentication and implement the appropriate session renewal flow.
API returns 403 User lacks required permission Review backend authorization and the user's assigned permissions.
Token disappears after restart Token was kept only in memory or storage failed Review secure-storage behavior and error handling.
Production build exposes a credential Private value was bundled into the client Revoke and rotate the credential, remove it from the app, and move the operation server-side.
Build uses old environment values Build cache or configuration selection issue Verify the selected profile and follow the tooling's rebuild or cache-clearing procedure.

24. Best Practices Checklist

  • Use one documented configuration strategy for the project.
  • Separate development, staging, and production settings.
  • Keep configuration access centralized and validate required values.
  • Never treat client-side environment variables as secret storage.
  • Keep privileged credentials on a trusted backend.
  • Use HTTPS and normal certificate validation.
  • Use secure storage for suitable sensitive values such as authentication tokens.
  • Keep API endpoints and authentication handling in reusable service modules.
  • Validate all security-sensitive input on the backend.
  • Enforce authorization for every protected operation.
  • Do not log passwords, tokens, private keys, or sensitive responses.
  • Use appropriate timeout and retry behavior.
  • Review dependencies and third-party SDK permissions.
  • Check release artifacts for accidentally bundled credentials.
  • Test the final Android and iOS release builds.
  • Document environment setup without publishing private values.

25. Interview Questions and Answers

Q1. What are environment variables in React Native?

Answer: Environment variables are configuration values supplied to the application or build process. They can define API URLs, environment names, and other settings that vary between development, staging, and production.

Q2. Are environment variables secure in a mobile app?

Answer: No. Values embedded in a distributed app may be extracted. Environment variables help with configuration, but they do not protect private credentials.

Q3. Why should API URLs be centralized?

Answer: Centralization reduces duplication, makes environment switching easier, improves consistency, and simplifies testing and maintenance.

Q4. What is the difference between development and production configuration?

Answer: Development configuration usually targets test services and supports diagnostics, while production configuration targets live services and applies stricter release and monitoring practices.

Q5. What is the purpose of an API client module?

Answer: An API client centralizes request construction, base URLs, headers, timeouts, authentication handling, and error processing.

Q6. Can a private API key be stored in a .env file?

Answer: It should not be included in a mobile application's distributed bundle. A .env file does not make an embedded value secret. Keep privileged keys on the backend.

Q7. What is the difference between authentication and authorization?

Answer: Authentication verifies identity. Authorization checks whether that identity is permitted to perform a particular action.

Q8. Why is AsyncStorage not suitable for sensitive tokens?

Answer: AsyncStorage is general-purpose persistent storage and is not designed to function as a secure credential vault. Appropriate platform-backed secure storage is a better option for sensitive tokens.

Q9. Why should API requests use HTTPS?

Answer: HTTPS encrypts communications in transit and allows the client to validate the server's identity using certificate trust rules.

Q10. What should happen when an API request times out?

Answer: The application should stop waiting where supported, display a safe recovery message, and retry only when the operation and failure type make retrying appropriate.

Q11. Why is client-side validation not enough?

Answer: A client can be modified or bypassed. The backend must independently validate input and enforce permissions.

Q12. What are feature flags?

Answer: Feature flags enable or disable application functionality. Because client-side flags can be changed, they must not replace server-side access control.

Q13. What is CI/CD's role in environment management?

Answer: CI/CD automates testing and building and can supply controlled environment configuration, reducing manual errors and improving release consistency.

Q14. How should an exposed private API key be handled?

Answer: Revoke or rotate the key, remove it from the client and relevant source history where appropriate, investigate misuse, and move privileged operations to a trusted backend.

Q15. How can a developer verify that a production build is secure?

Answer: Inspect the build for unintended credentials, confirm the production endpoint and HTTPS configuration, test backend authorization, review logs, and verify that development-only features are disabled or restricted.

26. Practical Exercises

  1. Create development, staging, and production configuration profiles for a sample application.
  2. Implement a centralized configuration module with validation for the API URL and timeout.
  3. Create an API client that handles HTTP status codes and timeouts consistently.
  4. Store an authentication token using an appropriate secure-storage library.
  5. Test API requests with valid, expired, and missing tokens.
  6. Inspect a release build to confirm that no private backend credentials are embedded.
  7. Implement safe error messages for network failures and server errors.
  8. Document the environment setup process for a new developer joining the project.

27. Quick Revision Notes

  • Environment variables manage configuration; they are not a secret vault.
  • Use different configuration values for development, staging, and production.
  • Centralize API configuration and validate required values.
  • Keep private API keys and privileged credentials on the backend.
  • Use HTTPS and maintain certificate validation.
  • Use secure storage for suitable sensitive values stored on the device.
  • Validate inputs and enforce permissions on the server.
  • Avoid logging tokens, passwords, or private information.
  • Use timeouts and carefully designed retry policies.
  • Inspect and test final release builds before distribution.

28. Conclusion

Managing environment variables, API configuration, and application security is an essential part of developing reliable React Native applications. A well-designed configuration system helps teams maintain separate deployment environments, while a centralized API layer makes networking consistent and easier to test.

Security requires more than moving values into environment files. Developers must understand which values are public, keep privileged credentials on the backend, store sensitive local data appropriately, use HTTPS, validate requests, and enforce authorization on every protected server operation. Combining these practices with automated tests, controlled CI/CD pipelines, and release reviews helps reduce security risks and makes applications easier to maintain.

29. Additional Learning Resources

whatsapp